namespace HZY.Core.Redis.Aop.Cache;

/// <summary>
/// 清除缓存AOP
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CacheEvictAttribute : BaseCacheAttribute
{
    private readonly Type _cacheClassType;
    private readonly string _actionName;

    public CacheEvictAttribute()
    {

    }

    /// <summary>
    /// 用于设置 默认 缓存key 需要 ，自动组合默认缓存名称
    /// </summary>
    /// <param name="cacheClassType">缓存 类 类型</param>
    /// <param name="actionName">缓存函数名称</param>
    public CacheEvictAttribute(Type cacheClassType, string actionName)
    {
        _cacheClassType = cacheClassType;
        _actionName = actionName;
    }

    /// <summary>
    /// 移除时机  默认方法执行完
    /// </summary>
    /// <value></value>
    public bool BeforeRun { get; set; } = false;


    public override void OnEntry(MethodContext context)
    {
        if (BeforeRun)
        {
            RemoveKey(context);
        }
    }

    public override void OnException(MethodContext context)
    {

    }

    public override void OnSuccess(MethodContext context)
    {

    }

    public override void OnExit(MethodContext context)
    {
        if (!BeforeRun)
        {
            RemoveKey(context);
        }
    }

    /// <summary>
    /// 移除key
    /// </summary>
    /// <param name="aopContext"></param>
    private void RemoveKey(MethodContext aopContext)
    {
        if (RedisServiceType == null)
        {
            RemoveMemoryKey(aopContext);
        }
        else
        {
            RemoveRedisKey(aopContext);
        }
    }

    /// <summary>
    /// 移除redis key
    /// </summary>
    /// <param name="aopContext"></param>
    private void RemoveRedisKey(MethodContext aopContext)
    {
        var cacheKeys = GetCacheKeys(aopContext);
        foreach (var cacheKey in cacheKeys)
        {
            if (cacheKey.Contains('*'))
            {
                RemoveRedisGroupKey(aopContext, cacheKey);
            }
            else
            {
                var redisCache = GetDatabase(aopContext);
                redisCache.KeyDelete(cacheKey);
            }
        }
    }

    /// <summary>
    /// 移除Redis分组的key
    /// </summary>
    /// <param name="aopContext"></param>
    /// <param name="cacheKey"></param>
    private void RemoveRedisGroupKey(MethodContext aopContext, string cacheKey)
    {
        var redisMultiplexer = GetMultiplexer(aopContext);
        List<RedisKey> redisKeys = new List<RedisKey>();
        var endPoints = redisMultiplexer.GetEndPoints();
        foreach (var endPoint in endPoints)
        {
            var server = redisMultiplexer.GetServer(endPoint);
            foreach (var key in server.Keys(pattern: cacheKey))
            {
                redisKeys.Add(key);
            }
        }

        var redisCache = GetDatabase(aopContext);
        redisCache.KeyDelete(redisKeys.ToArray());
    }

    /// <summary>
    /// 移除内存key
    /// </summary>
    /// <param name="aopContext"></param>
    private void RemoveMemoryKey(MethodContext aopContext)
    {
        var cacheKeys = GetCacheKeys(aopContext);
        var cache = this.GetService<IMemoryCache>(aopContext);

        foreach (var cacheKey in cacheKeys)
        {
            // 是否要进行前缀或后缀匹配
            if (cacheKey.StartsWith('*'))
            {
                var pattern = cacheKey.Substring(1, cacheKey.Length - 1);
                RemoveMemoryGroupKey(pattern, cache, true);
            }
            else if (cacheKey.EndsWith('*'))
            {
                var pattern = cacheKey.Substring(0, cacheKey.Length - 1);
                RemoveMemoryGroupKey(pattern, cache, false);
            }
            else
            {
                cache.Remove(cacheKey);
            }
        }
    }

    /// <summary>
    /// 移除内存缓存分组的 key
    /// </summary>
    /// <param name="pattern"></param>
    /// <param name="memoryCache"></param>
    /// <param name="isStart"></param>
    private void RemoveMemoryGroupKey(string pattern, IMemoryCache memoryCache, bool isStart)
    {
        // .NET6.0之前直接使用_entries字段
        // .NET6.0.35开始MemoryCache内部缓存分为使用字符串和使用对象的列表缓存两种
        // .NET7开始将缓存包装到CoherentState对象中，这这对象里使用_entries字段
        // .NET8.0.10开始将CoherentState对象中的_entries字段改为StringEntriesCollection和ObjectEntriesCollection两种缓存
        IDictionary? entries = null;
#if NET8_0_OR_GREATER
        var coherentState = memoryCache.GetType().GetField("_coherentState", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(memoryCache);

        entries = coherentState?.GetType().GetField("_stringEntries", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(coherentState) as IDictionary;

        if (entries == null)
        {
            entries = coherentState?.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(coherentState) as IDictionary;
        }
#elif NET7_0_OR_GREATER
        var coherentState = memoryCache.GetType().GetField("_coherentState", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(memoryCache);
        entries = coherentState?.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(coherentState) as IDictionary;
#else
            entries = memoryCache.GetType().GetField("_stringKeyEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(memoryCache) as IDictionary;
            if (entries == null)
            {
                entries = memoryCache.GetType().GetField("_entries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(memoryCache) as IDictionary;
            }
#endif
        if (entries == null)
        {
            throw new Exception("前缀或尾缀匹配不可用!");
        }

        List<string> keys = new List<string>();

        if (isStart)
        {
            // 前缀模式匹配
            foreach (var key in entries.Keys)
            {
                var k = key as string;
                if (k != null)
                {
                    if (k.StartsWith(pattern))
                    {
                        keys.Add(k);
                    }
                }
            }
        }
        else
        {
            // 尾缀模式匹配
            foreach (var key in entries.Keys)
            {
                var k = key as string;
                if (k != null)
                {
                    if (k.EndsWith(pattern))
                    {
                        keys.Add(k);
                    }
                }
            }
        }

        foreach (var key in keys)
        {
            memoryCache.Remove(key);
        }
    }

    private IConnectionMultiplexer GetMultiplexer(MethodContext aopContext)
    {
        var redisService = this.GetService(aopContext, RedisServiceType);
        var propertyInfos = redisService.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
        var databasePropertyInfo = propertyInfos.FirstOrDefault(w => w.PropertyType == typeof(IConnectionMultiplexer));
        if (databasePropertyInfo == null)
        {
            throw new Exception($"Redis服务 {RedisServiceType.FullName} 找不到属性 public IConnectionMultiplexer {nameof(IConnectionMultiplexer)} {{get;}}");
        }
        // 获取 idatabase
        var redisCache = (IConnectionMultiplexer)databasePropertyInfo.GetValue(redisService);
        if (redisCache == null)
        {
            throw new Exception($"Redis服务 {RedisServiceType.FullName} 属性 public IConnectionMultiplexer {nameof(IConnectionMultiplexer)} {{get;}} 是 null ");
        }

        return redisCache;
    }

    /// <summary>
    /// 获取要删除的缓存Key列表
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    protected List<string> GetCacheKeys(MethodContext context)
    {
        if (_cacheClassType != null && !string.IsNullOrWhiteSpace(_actionName))
        {
            return [_cacheClassType.FullName + "." + _actionName];
        }

        if (!string.IsNullOrWhiteSpace(CacheKey))
        {
            return ParseCacheKeys(context).Distinct().ToList(); ;
        }

        var name = context.Method.Name;
        var prefix = context.Method.DeclaringType?.FullName;
        return [$"{prefix}.{name}"];
    }

    /// <summary>
    /// 解析缓存主键
    /// </summary>
    /// <param name="context">拦截器上下文</param>
    private List<string> ParseCacheKeys(MethodContext context)
    {
        //寻找大括号{} 内的内容
        var replaceParameters = Regex.Matches(CacheKey, @"\{(.+?)\}").Select(m => m.Groups[1].Value).ToList();
        if (replaceParameters.Count == 0)
        {
            return [CacheKey];
        }

        // 获取所有方法参数
        var typeNames = context.Method.GetParameters().Select(t => t.Name).ToList();
        foreach (var replaceParameter in replaceParameters)
        {
            var firstName = replaceParameter.Split(".")[0];
            // 查询括号里的内容  .分割后第一位  能和参数名对的上吗
            if (!typeNames.Contains(firstName))
            {
                continue;
            }

            // 获取这个值对应的索引
            int index = typeNames.FindIndex(typeName => typeName == firstName);
            // 对内容进行分割
            var names = replaceParameter.Split(".").ToList();

            var replaceName = "{" + replaceParameter + "}";

            ParameterValue parameterValue;

            // 分割后大于长度大于一 说明存在实例内部属性调用
            if (names.Count > 1)
            {
                // 移除第一位
                names.RemoveAt(0);
                // 递归获取到属性值
                parameterValue = GetParameterValues(context.Arguments[index], names);
            }
            else
            {
                parameterValue = GetArguments(context.Arguments[index]);
            }

            // 如果属性只不是集合，继续替换Key
            if (!parameterValue.IsEnumerable)
            {
                CacheKey = CacheKey.Replace(replaceName, parameterValue.Values[0]);
                continue;
            }

            // 如果是集合类型，并且只有一个参数，直接替换成Key列表
            // 如果是集合类型，但有多个参数，抛出异常
            if (replaceParameters.Count == 1)
            {
                return parameterValue.Values.Select(value => CacheKey.Replace(replaceName, value)).ToList();
            }
            else
            {
                throw new Exception("不支持多个参数中包含集合类型数据");
            }
        }

        return [CacheKey];
    }

    /// <summary>
    /// 获取参数
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    protected ParameterValue GetArguments(object? obj)
    {
        if (obj == null)
        {
            return ParameterValue.Default;
        }

        // 字符串要单独处理，因为它也是 IEnumerable
        if (obj is string str)
        {
            return new ParameterValue()
            {
                IsEnumerable = false,
                Values = [str]
            };
        }

        if (obj is IEnumerable enumerable)
        {
            var values = enumerable.Cast<object>()
                .Select(t => t?.ToString())
                .Where(w => !string.IsNullOrWhiteSpace(w))
                .ToList();
            return new ParameterValue()
            {
                IsEnumerable = true,
                Values = values
            };
        }

        return new ParameterValue()
        {
            IsEnumerable = false,
            Values = [obj.ToString()]
        };
    }

    /// <summary>
    /// 递归获取属性值
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="parameterNames"></param>
    /// <returns></returns>
    protected ParameterValue GetParameterValues(object obj, List<string> parameterNames)
    {
        if (obj == null)
        {
            return ParameterValue.Default;
        }

        if (parameterNames.Count > 1)
        {
            var name = parameterNames[0];
            parameterNames.RemoveAt(0);

            // 获取属性值
            var propertyInfo = GetPropertyInfo(obj, name);
            if (propertyInfo != null)
            {
                return GetParameterValues(propertyInfo.GetValue(obj), parameterNames);
            }
            return ParameterValue.Default;
        }
        else if (parameterNames.Count == 1)
        {
            // 获取属性值
            var propertyInfo = GetPropertyInfo(obj, parameterNames[0]);

            if (propertyInfo == null)
            {
                return ParameterValue.Default;
            }

            // 字符串要单独处理，因为它也是 IEnumerable
            if (propertyInfo.PropertyType.IsAssignableTo(typeof(string)))
            {
                return new ParameterValue()
                {
                    IsEnumerable = false,
                    Values = [propertyInfo.GetValue(obj)?.ToString()]
                };
            }

            // 如果是列表
            if (propertyInfo.PropertyType.IsAssignableTo(typeof(IEnumerable)))
            {
                var enumerables = propertyInfo.GetValue(obj) as IEnumerable;
                var values = enumerables?.Cast<object>()
                    .Select(t => t?.ToString())
                    .Where(w => !string.IsNullOrWhiteSpace(w))
                    .ToList();

                return new ParameterValue()
                {
                    IsEnumerable = true,
                    Values = values
                };
            }

            // 从对象里获取这个属性
            return new ParameterValue()
            {
                IsEnumerable = false,
                Values = [propertyInfo.GetValue(obj)?.ToString()]
            };
        }
        else
        {
            return ParameterValue.Default;
        }
    }

    protected class ParameterValue
    {
        public bool IsEnumerable { get; set; }

        public List<string> Values { get; set; }

        public static ParameterValue Default => new ParameterValue
        {
            IsEnumerable = false,
            Values = ["null"]
        };
    }
}